#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "securec.h"
#include "cmsis_os2.h"
#include "ohos_init.h"
#include "ohos_types.h"
#include "wifi_device.h"
#include "wifi_error_code.h"
#include "wifi_linked_info.h"
#include "station_info.h"
#include "wifi_event.h"
#include "wifi_error_code.h"
#include "wifi_sta.h"
#include "esp_wifi_types.h"
#include "utils_log.h"

#define DEF_TIMEOUT 30
#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK


static int g_wifiConnectStatus = WIFI_STATE_INIT;
static WifiEvent g_wifiEventHandler = {0};
static int g_networkId = 0;

#ifndef MAC2STR
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#endif
#ifndef MACSTR
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x "
#endif

#define MAX_SCAN_LIST_LEN   32
#define MIN_SAME_WIFI_NAME_LIST_SIZE  2      //同名WiFi数组的最小个数

static int WaitConnectResult(void)
{
    int connectTimeout = DEF_TIMEOUT;
    while (connectTimeout > 0) {
        sleep(1);
        connectTimeout--;
        if (g_wifiConnectStatus == WIFI_STATE_CONNECTED) {
            printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - connectTimeout));
            break;
        }
    }

    if (connectTimeout <= 0) {
        printf("WaitConnectResult:timeout!\n");
        return 0;
    }
    return 1;
}

static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info)
{
    (void)info;
    if (state > 0) {
        g_wifiConnectStatus = WIFI_STATE_CONNECTED;
        printf("callback function for wifi connect\r\n");
    } else {
        g_wifiConnectStatus = WIFI_STATE_DISCONNECTED;
        if (info->disconnectedReason != 8) {//8是主动断连
            //非主动触发的断连，切换成空白的连接wifi
            SetWifiConfigWithEmptyBssid(g_networkId);
        }
        printf("connect error:%d, please check wifi config, state:%d, try connect again\r\n", info->disconnectedReason,state);
    }
}

int InitWifiSta(void)
{
    printf("<--Wifi Sta Init-->\r\n");
    g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler;
    WifiErrorCode ret = RegisterWifiEvent(&g_wifiEventHandler);
    if (ret != WIFI_SUCCESS) {
        printf("register wifi event fail!\r\n");
        LOG_TO_FILE("register wifi event fail!");
    } else {
        printf("register wifi event succeed!\r\n");
    }

    ret = EnableWifi();
    if (ret != WIFI_SUCCESS && ret != ERROR_WIFI_BUSY) {
        printf("EnableWifi failed, wifi_error = %d\r\n", ret);
        LOG_TO_FILE("EnableWifi failed, wifi_error = %d", ret);
        return -1;
    }

    return 0;
}

int ConnectToAP(const char *ssid, const char *psw)
{
    if(NULL==ssid || NULL==psw) {
       printf("[%s-%d]:input params is invalid\r\n",__func__, __LINE__);
       LOG_TO_FILE("input params is invalid");
       return -1;
    }

    if(InitWifiSta() != 0) {
        printf("Init Wifi station failed.\n");
        LOG_TO_FILE("Init Wifi station failed.");
        return -1;
    }
    
    if (IsWifiActive() == 0) {
        printf("Wifi station is not actived.\n");
        LOG_TO_FILE("Wifi station is not actived.");
        return -1;
    }

    if(IsWifiConnect() == true) {
        printf("Wifi connected need to disconnect.\r\n");
        Disconnect();
    }

    WifiDeviceConfig apConfig = {0};

    strcpy_s(apConfig.ssid, sizeof(apConfig.ssid), ssid);
    strcpy_s(apConfig.preSharedKey, sizeof(apConfig.preSharedKey), psw);
    printf("Wifi ssid:%s, psw:%s \r\n", apConfig.ssid, apConfig.preSharedKey);
    apConfig.securityType = SELECT_WIFI_SECURITYTYPE;
    if (AddDeviceConfig(&apConfig, &g_networkId) == WIFI_SUCCESS) {
        if (ConnectTo(g_networkId) == WIFI_SUCCESS && WaitConnectResult() == 1) {
            printf("WiFi connect succeed!\r\n");
        } else {
            printf("connecting fail\r\n");
            return -1;
        }
    }

    esp_wifi_set_ps(WIFI_PS_NONE);
    return 0;
}

void DisconnectWifi()
{
    if(g_wifiConnectStatus != WIFI_STATE_CONNECTED) {
        return;
    }

    UnRegisterWifiEvent(&g_wifiEventHandler);
    Disconnect();
    DisableWifi();
}

WIFI_STATUS GetWifiStatus(void) 
{
    return g_wifiConnectStatus;
}

bool IsWifiConnect()
{    
    if(WIFI_STATE_CONNECTED == g_wifiConnectStatus){
        WifiLinkedInfo info;
        memset(&info, 0, sizeof(info));
        int ret = GetLinkedInfo(&info);
        if (ret != WIFI_SUCCESS){
            return false;
        }

        if (info.ipAddress > 0){
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

//依据连续强于当前rssi次数,切换热点,-50以上信号较好可不切热点
//-50 -- -60  -- 4
//-60 -- -70  -- 3
//-70 -- -80  -- 2
//-80 -- -1000  -- 1
//考虑评分模式调优,避免频繁切换wifi
static int g_notBestCount = 0;

static bool isBetterChange(int curRssi, int bestRssi)
{
    if (curRssi >= -50 || curRssi >= bestRssi) {
        //如果当前热点经常是最好的,需要减少切换次数
        if (g_notBestCount > 0) {
            g_notBestCount--;
        }
        return false;
    }
    if (curRssi < -80) {
        return true;
    } else if(curRssi < -70) {
        g_notBestCount++;
        return g_notBestCount >= 2?true:false;
    } else if(curRssi < -60) {
        g_notBestCount++;
        return g_notBestCount >= 3?true:false;
    }else if(curRssi < -50) {
        g_notBestCount++;
        return g_notBestCount >= 4?true:false;
    }
    return false;
}

int GetScanSameWifiNameInfoList(const char *ssid, WifiScanInfo *infoList)
{
    if (NULL==ssid || NULL==infoList){
        printf("[%s-%d]:input params is invalid\r\n",__func__, __LINE__);
        LOG_TO_FILE("input params is invalid");
        return -1;
    }
    
    WifiErrorCode ret = Scan();
    if (ret != WIFI_SUCCESS) {
        printf("[%s-%d]:Scan failed, ret = %d\n",__func__, __LINE__,ret);
        LOG_TO_FILE("Scan failed, ret = %d", ret);
        return -1;
    }
    //等待扫描结束,才能查找到扫描列表
    // printf("---wait for scan success end---");
    osDelay(1000);
  
    WifiScanInfo scanfList[MAX_SCAN_LIST_LEN] = {0};
    int len = MAX_SCAN_LIST_LEN;
    ret = GetScanInfoList(scanfList, &len);
    if (ret != WIFI_SUCCESS) {
        printf("[%s-%d]:GetScanInfoList failed, ret = %d \n",__func__, __LINE__, ret);
        LOG_TO_FILE("GetScanInfoList failed, ret = %d", ret);
        return -1;
    }

    //printf("[%s-%d]:scanfList list len :%d\n", __func__, __LINE__, len);

    int listLen = 0;
    for (int i=0; i<len; i++){ //选择给定的优选wifi的ssid
       if (strcmp(ssid, scanfList[i].ssid) == 0){
            memcpy(&infoList[listLen], &scanfList[i], sizeof(WifiScanInfo));
            listLen++;
       }

        // printf("===== NO.%d ap info =====\n", i);
        // printf("---- ssid is %s ----\n", scanfList[i].ssid);
        // printf("---- bssid is  " MACSTR "  ----\n", MAC2STR(scanfList[i].bssid));
        // printf("---- securityType is %d ----\n", scanfList[i].securityType);
        // printf("---- rssi is %d ----\n", scanfList[i].rssi);
        // printf("---- band is %d ----\n", scanfList[i].band);
        // printf("---- frequency is %d ----\n", scanfList[i].frequency);//后续调优可考虑频道数量
    }
    
    return listLen;
}

int ConnectBestWifi(const char *ssid, const char *password)
{
    if(NULL==ssid || NULL==password) {
       printf("[%s-%d]:input params is invalid\r\n",__func__, __LINE__);
       LOG_TO_FILE("input params is invalid");
       return -1;
    }

    WifiLinkedInfo linkedInfo;
    memset(&linkedInfo, 0, sizeof(WifiLinkedInfo));
    if(GetLinkedInfo(&linkedInfo) != WIFI_SUCCESS) {
        printf("no need to choice best wifi\n");
        return -1;
    }

    WifiScanInfo sameNameList[MAX_SCAN_LIST_LEN] = {0};
    int ret = GetScanSameWifiNameInfoList(linkedInfo.ssid, sameNameList);
    if (ret <= 0) {  //依据当前连接状态,扫描获取可选ap
        printf("Cant get Scan List!!!\n");
        return -1;
    }
    printf("[%s-%d]:scanSameNameList list len :%d\n", __func__, __LINE__, ret);
    if (ret < MIN_SAME_WIFI_NAME_LIST_SIZE){ //同名WiFi个数小于2则不需要WiFi优选选择
        return 0;
    }

    //扫描成功后,进行优选wifi
    int bestIndex = 0;
    int nowRssi = -1000;    //rssi最小值
    for (int i=0; i<ret; i++){
        if(sameNameList[i].rssi > sameNameList[bestIndex].rssi){
            bestIndex = i;
        }

        if ((memcmp(linkedInfo.bssid, sameNameList[i].bssid, sizeof(linkedInfo.bssid)) == 0)) {
            nowRssi = linkedInfo.rssi;
        }    
    }
    printf("best wifi bssid: " MACSTR " , best pos is %d.\n", MAC2STR(sameNameList[bestIndex].bssid), bestIndex);
    if (!isBetterChange(nowRssi, sameNameList[bestIndex].rssi)) { //拿当前ap的rssi与最好的rssi进行比较
        return 0;
    }
    g_notBestCount = 0;

    //不选定bssid的连接会自动连接最先扫到的ap(一般是信号最好的ap先被扫到),
    //所以利用断连重连机制尝试连上当前最好wifi
    if (linkedInfo.connState == WIFI_CONNECTED) {
        printf("current Wifi not the best, need to disconnect.\n");
        Disconnect();
    }

    //利用断连机制，而不主动连接
    WifiDeviceConfig apConfig = {0};
    memcpy_s(apConfig.ssid, sizeof(apConfig.ssid), ssid, strlen(ssid));
    memcpy_s(apConfig.preSharedKey, sizeof(apConfig.preSharedKey), password, strlen(password));
    memcpy_s(apConfig.bssid, sizeof(apConfig.bssid), sameNameList[bestIndex].bssid, sizeof(sameNameList[bestIndex].bssid));
    apConfig.securityType = SELECT_WIFI_SECURITYTYPE;
    apConfig.freq =  sameNameList[bestIndex].frequency;

    printf("going to connect wifi bssid: " MACSTR " .\r\n", MAC2STR(sameNameList[bestIndex].bssid));
    int networkId = 0;
    if (AddDeviceConfig(&apConfig, &networkId) == WIFI_SUCCESS) {
        if (ConnectToBssid(networkId) == WIFI_SUCCESS) {//wifi_device适配未选定bssid
            printf("WiFi connect succeed!\r\n");
        } else {
            printf("WiFi connecting failed\r\n");
            return -1;;
        }
    }

    return 0;
}